import threading
import os
from typing import List

from utils.CustomException import CantConvertToBoolException
from utils.PropertiesUtils import PropertiesUtils
from utils.DateUtils import DateFormatEnum


def str_to_bool(filed: str) -> bool:
    """
    将str类型转化为bool类型
    :param filed: 转换的字段
    :return: bool类型
    """
    if filed == "false" or filed == "False":
        return False
    elif filed == "true" or filed == "True":
        return True
    else:
        raise CantConvertToBoolException(f"不能将{filed}转换为bool类型")


class Constants:
    """
    常量类\n
    采用单例模式\n
    从property读取配置加载到内存中\n
    """
    _instance = None
    _lock = threading.Lock()

    def __init__(self):
        self.package_to_path_map = {}

        self.WORK_ROOT_PATH = Constants.get_root_path()

        self.properties_utils = PropertiesUtils(self.WORK_ROOT_PATH +  "resources/application.properties")
        self.IGNORE_TABLE_PREFIX = str_to_bool(
            self.properties_utils.get_property_by_key("ignore.table.prefix")
        )

        self.DATABASE_PORT = int(self.properties_utils.get_property_by_key("db.port"))
        self.DATABASE_NAME = self.properties_utils.get_property_by_key("db.name")
        self.DATABASE_USER = self.properties_utils.get_property_by_key("db.username")
        self.DATABASE_PASSWORD = self.properties_utils.get_property_by_key("db.password")

        self.SUFFIX_BEAN_PARAM = self.properties_utils.get_property_by_key("suffix.bean.param")
        self.SUFFIX_BEAN_PARAM_FUZZY = self.properties_utils.get_property_by_key("suffix.bean.param.fuzzy")
        self.SUFFIX_BEAN_PARAM_TIME_START = self.properties_utils.get_property_by_key("suffix.bean.param.time.start")
        self.SUFFIX_BEAN_PARAM_TIME_END = self.properties_utils.get_property_by_key("suffix.bean.param.time.end")
        self.SUFFIX_MAPPERS = self.properties_utils.get_property_by_key("suffix.mappers")

        self.PROJECT_AUTHOR = self.properties_utils.get_property_by_key("project.author")

        self.OUTPUT_PATH = self.properties_utils.get_property_by_key("path.output")
        self.JAVA_BASE_PATH = self.__connect_path(self.OUTPUT_PATH, "src/main/java")
        self.MAIN_BASE_PATH = self.__connect_path(self.OUTPUT_PATH, "src/main")
        # 这里是指输出到固定的src/main/java目录下

        self.IGNORE_BEAN_TOJSON_FILED = self.__generate_ignore_filed_to_list()
        self.IGNORE_BEAN_TOJSON_CLASS = self.properties_utils.get_property_by_key("ignore.bean.tojson.class")

        self.BEAN_DATE_FORMAT_FORM = self.__covert_form_name_to_form_enum()

        self.BEAN_DATE_FORMAT_EXPRESSION = self.properties_utils.get_property_by_key("bean.date.format.expression")
        self.BEAN_DATE_FORMAT_CLASS = self.properties_utils.get_property_by_key("bean.date.format.class")

        self.BEAN_DATE_UNFORMAT_EXPRESSION = self.properties_utils.get_property_by_key("bean.date.unformat.expression")
        self.BEAN_DATE_UNFORMAT_CLASS = self.properties_utils.get_property_by_key("bean.date.unformat.class")

        self.PACKAGE_BASE = self.properties_utils.get_property_by_key("package.base")

        self.PACKAGE_PO = self.__connect_package(self.properties_utils.get_property_by_key("package.po"))
        self.PACKAGE_PARAM = self.__connect_package(self.properties_utils.get_property_by_key("package.param"))
        self.PACKAGE_UTILS = self.__connect_package(self.properties_utils.get_property_by_key("package.utils"))
        self.PACKAGE_ENUM = self.__connect_package(self.properties_utils.get_property_by_key("package.enum"))
        self.PACKAGE_MAPPERS = self.__connect_package(self.properties_utils.get_property_by_key("package.mappers"))
        self.PACKAGE_SERVICE = self.__connect_package(self.properties_utils.get_property_by_key("package.service"))
        self.PACKAGE_VO = self.__connect_package(self.properties_utils.get_property_by_key("package.vo"))
        self.PACKAGE_EXCEPTION = self.__connect_package(self.properties_utils.get_property_by_key("package.exception"))
        self.PACKAGE_CONTROLLER = self.__connect_package(
            self.properties_utils.get_property_by_key("package.controller"))
        self.PACKAGE_SERVICE_IMPL = self.__connect_package(
            self.properties_utils.get_property_by_key("package.service.impl"))

        self.PO_PATH = self.__package_name_to_path(self.PACKAGE_PO)
        self.PARAM_PATH = self.__package_name_to_path(self.PACKAGE_PARAM)
        self.UTILS_PATH = self.__package_name_to_path(self.PACKAGE_UTILS)
        self.ENUM_PATH = self.__package_name_to_path(self.PACKAGE_ENUM)
        self.MAPPERS_PATH = self.__package_name_to_path(self.PACKAGE_MAPPERS)
        self.SERVICE_PATH = self.__package_name_to_path(self.PACKAGE_SERVICE)
        self.VO_PATH = self.__package_name_to_path(self.PACKAGE_VO)
        self.EXCEPTION_PATH = self.__package_name_to_path(self.PACKAGE_EXCEPTION)
        self.CONTROLLER_PATH = self.__package_name_to_path(self.PACKAGE_CONTROLLER)
        self.SERVICE_IMPL_PATH = self.__package_name_to_path(self.PACKAGE_SERVICE_IMPL)

        self.RESOURCES_PATH = self.__connect_path(self.MAIN_BASE_PATH, "resources")
        self.MAPPERS_XML_PATH = self.__connect_path(self.RESOURCES_PATH, self.PACKAGE_MAPPERS).replace(".", "/") + "/"
        self.__add_package_to_package_map(self.PACKAGE_MAPPERS, self.MAPPERS_PATH)

        self.TEMPLATES_PATH = self.WORK_ROOT_PATH + "resources/templates/"

    @staticmethod
    def get_instance():
        """
        返回单例\n
        :return:单例
        """
        if Constants._instance is None:
            with Constants._lock:
                Constants._instance = Constants()
        return Constants._instance

    @staticmethod
    def __connect_path(base_path: str, path: str) -> str:
        """
        根据根目录连接目录名
        :param base_path:根目录
        :param path: 要连接的目录
        :return: 目录名
        """
        return os.path.join(base_path, path).replace("\\", "/")

    def __connect_package(self, package_name: str) -> str:
        """
        根据根包名连接包名
        :param package_name: 包名
        :return: 包名
        """
        return self.PACKAGE_BASE + "." + package_name

    def __package_name_to_path(self, package_name: str) -> str:
        """
        包名转换为路径名
        :param package_name: 包名
        :return: 路径名
        """
        path = self.__connect_path(self.JAVA_BASE_PATH, package_name.replace(".", "/")) + "/"
        self.__add_package_to_package_map(package_name, path)
        return path

    def __add_package_to_package_map(self, package_name: str, path: str) -> None:
        self.package_to_path_map[package_name] = path

    def __generate_ignore_filed_to_list(self) -> List[str]:
        """
        生成忽略字段成为列表
        :return: 字段列表
        """
        filed_list = self.properties_utils.get_property_by_key("ignore.bean.tojson.filed").split(",")
        for i in range(len(filed_list)):
            filed_list[i] = filed_list[i].replace(" ", "")
        return filed_list

    def __covert_form_name_to_form_enum(self) -> DateFormatEnum:
        form_name = self.properties_utils.get_property_by_key("bean.date.format.form")
        for i in DateFormatEnum:
            if form_name == i.name:
                return i
        raise TypeError(f"{form_name}为非法时间枚举类型")

    def get_path_by_package(self, package_name: str) -> str:
        """
        根据包名查询路径名
        :param package_name:包名
        :return: 路径名
        """
        if package_name not in self.package_to_path_map.keys():
            raise Exception(f"包名{package_name}不存在!")
        else:
            return self.package_to_path_map[package_name]

    def get_package_by_path(self, path_name: str) -> str:
        """
        根据路径查询包名
        :param path_name:路径名
        :return: 包名
        """
        if path_name not in self.package_to_path_map.values():
            raise Exception(f"路径名{path_name}不存在")
        else:
            index = list(self.package_to_path_map.values()).index(path_name)
            return list(self.package_to_path_map.keys())[index]

    @staticmethod
    def get_root_path(path: str = "") -> str:
        if path == "":
            current_path = os.getcwd()
        else:
            current_path = os.path.dirname(path)
        if "config.json" not in os.listdir(current_path):
            return Constants.get_root_path(current_path)
        else:
            return current_path.replace("\\", "/") + "/"


if __name__ == "__main__":
    print(Constants.get_root_path())
